package com.mantou.proxyadapter.pool;

import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.http.HttpHost;
import org.apache.http.client.methods.CloseableHttpResponse;

import com.mantou.proxyadapter.net.HttpClientDownloader;

public class DomainPool {

	private SourcePool sourcePool = SourcePool.getInstance();

	private List<String> testurls = new ArrayList<String>();

	private HashMap<String,ProxyHolder> domainpool = new HashMap<String,ProxyHolder>();

	private ExecutorService threadpool = new ThreadPoolExecutor(30, 50, 0, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(),
			Executors.defaultThreadFactory(), 
			new RejectedExecutionHandler(){

		@Override
		public void rejectedExecution(Runnable r,
				ThreadPoolExecutor executor) {
			// TODO Auto-generated method stub
			System.out.println("reject exception,what is out thread? "+Thread.currentThread().getName());
			if(!executor.isShutdown()){
				r.run();
			}
		}

	});

	public DomainPool addTestUrl(String ... urls){
		for(String url:urls){
			testurls.add(url);
		}
		return this;
	}
	public DomainPool addTestUrl(List<String> urls){
		for(String url:urls){
			testurls.add(url);
		}
		return this;
	}

	private class DownloadThread implements Runnable{

		private ProxyHolder holder;
		private List<ProxyHolder> ret;
		private CleanConfig cleanConfig;
		private String url;

		public DownloadThread(ProxyHolder holder,List<ProxyHolder> ret,CleanConfig cleanConfig,String url) {
			super();
			this.holder = holder;
			this.ret = ret;
			this.cleanConfig = cleanConfig;
			this.url = url;
		}


		@Override
		public void run() {
			// TODO Auto-generated method stub
			HttpClientDownloader downloader = new HttpClientDownloader();
			CloseableHttpResponse download =null;
			Proxy proxy = sourcePool.get(holder.proxykey);
			long starttime = System.currentTimeMillis();
			try {
				download= downloader.download(url,new HttpHost(InetAddress.getByName(proxy.getIp()), proxy.getPort()));
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			if(download != null && holder.acceptCode.contains(download.getStatusLine().getStatusCode())){
				holder.totaldelay.addAndGet(System.currentTimeMillis() - starttime);
				if(url.startsWith("https:")){
					holder.supportHttps = true;
				}
				holder.passnum.addAndGet(1);
				holder.brownum.decrementAndGet();
				System.out.println("pass:"+proxy.getIp()+":"+proxy.getPort()+"----->"+url);
			}
			if(holder.brownum.get()<=0 && holder.passnum.get()>0){
				double passrate = (double)holder.passnum.get() / holder.totalnum.get();
				long avgdelay = holder.totaldelay.get() / holder.passnum.get();
				if(passrate >= cleanConfig.getConnectrate() && avgdelay >= cleanConfig.getDelay()){
					holder.delay = avgdelay;
					holder.enable = true;
					holder.passrate = passrate;
					if(!ret.contains(holder))
						ret.add(holder);
				}
			}

		}

	}

	private List<ProxyHolder> innerClean(List<Proxy> source,CleanConfig cleanConfig){

		List<ProxyHolder> ret = new ArrayList<DomainPool.ProxyHolder>();
		if(testurls.size() <=0)
			return ret;
		List<ProxyHolder> tempHolder = new ArrayList<DomainPool.ProxyHolder>();
		for(Proxy proxy:source){
			ProxyHolder holder = new ProxyHolder();
			tempHolder.add(holder);
			holder.enable = false;
			holder.proxykey = proxy.getKey();
			holder.acceptCode = cleanConfig.getAcceptCode();
			holder.delay = cleanConfig.getDelay();
			holder.supportHttps = false;
			holder.brownum =new AtomicInteger(0);
			holder.passnum =new AtomicInteger(0);
			holder.totalnum =new AtomicInteger(0);
			holder.totaldelay = new AtomicLong(0);
			for(String url:testurls){
				holder.brownum.addAndGet(1);
				holder.totalnum.addAndGet(1);
				threadpool.execute(new DownloadThread(holder,ret,cleanConfig,url));
			}
		}
		threadpool.shutdown();
		try {
			threadpool.awaitTermination(source.size() * 5, TimeUnit.SECONDS);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		for(ProxyHolder holder:tempHolder){
			if(holder.passnum.get()>0){
				double passrate = (double)holder.passnum.get() / holder.totalnum.get();
				long avgdelay = holder.totaldelay.get() / holder.passnum.get();
				if(passrate >= cleanConfig.getConnectrate() && avgdelay >= cleanConfig.getDelay()){
					holder.delay = avgdelay;
					holder.enable = true;
					holder.passrate = passrate;
					if(!ret.contains(holder))
						ret.add(holder);
				}
			}
		}
		System.out.println("clean end--->"+ret.size());
		return ret;
	}

	public DomainPool refresh(){
		if(testurls.size() <=0)
			return this;
		Iterator<Entry<String, ProxyHolder>> iterator = domainpool.entrySet().iterator();
		while(iterator.hasNext()){
			ProxyHolder holder = iterator.next().getValue();
			HttpClientDownloader downloader = new HttpClientDownloader();
			int passnum = 0;
			long totaldelay=0;
			for(String url:testurls){
				CloseableHttpResponse download = null;
				Proxy proxy = sourcePool.get(holder.proxykey);
				long starttime = System.currentTimeMillis();
				try {
					download = downloader.download(url,new HttpHost(InetAddress.getByName(proxy.getIp()), proxy.getPort()));
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				if(download != null && holder.acceptCode.contains(download.getStatusLine().getStatusCode())){
					totaldelay+=System.currentTimeMillis() - starttime;
					if(url.startsWith("https:")){
						holder.supportHttps = true;
					}
					passnum++; 
				}
			}
			if(passnum <=0){
				holder.enable = false;
				holder.passrate =0;
				holder.delay = Integer.MAX_VALUE /3;
			}
			holder.passrate =  (double)passnum / testurls.size();
			long avgdelay = totaldelay / passnum;
			holder.delay = avgdelay;
			holder.enable = true;
		}
		sourcePool.feedback(domainpool.values());
		return this;
	}

	public DomainPool clean(CleanConfig cleanConfig){
		domainpool.clear();
		int neednumber = cleanConfig.getNumber();
		List<Proxy> list = sourcePool.get(neednumber);
		List<ProxyHolder> ret = innerClean(list, cleanConfig);
		sourcePool.feedback(ret);
		for(ProxyHolder holder:ret){
			domainpool.put(holder.proxykey, holder);
		}
		return this;
	}

	public List<Proxy> get(int num){
		List<Proxy> ret= new ArrayList<Proxy>();
		List<ProxyHolder> proxys = new ArrayList<ProxyHolder>(domainpool.values());
		Collections.sort(proxys);
		List<ProxyHolder> subList = proxys.subList(0, proxys.size() >=num?num:proxys.size());
		for(ProxyHolder holder:subList){
			ret.add(sourcePool.get(holder.proxykey));
		}
		return ret;
	}
	public class ProxyHolder implements Comparable<ProxyHolder>{
		public boolean enable =false;
		public boolean supportHttps = false;
		public long delay;
		public double passrate;
		public String proxykey;
		public List<Integer> acceptCode = new ArrayList<Integer>(200);

		public AtomicInteger brownum;
		public AtomicInteger totalnum;
		public AtomicInteger passnum;
		public AtomicLong totaldelay;
		@Override
		public int compareTo(ProxyHolder o) {
			// TODO Auto-generated method stub
			if(passrate != o.passrate){
				return ((int)(o.passrate -passrate))*100;
			}
			return (int)(delay - o.delay);
		}
	}
}
